home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / Documentation / Tech Notes & Articles / Recipes / UI & Events / Dialogs < prev    next >
Encoding:
Text File  |  1995-11-07  |  13.6 KB  |  456 lines  |  [TEXT/ttxt]

  1. OpenDoc™ Recipes
  2.  
  3. Dialogs
  4. By The OpenDoc Design Team
  5. November, 1995
  6.  
  7.  
  8. © 1993-1995  Apple Computer, Inc. All Rights Reserved.
  9. Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
  10. Mac and OpenDoc are trademarks of Apple Computer, Inc. 
  11.  
  12.  
  13. Alerts and Modal Dialogs
  14.  
  15. In the case of alerts and modal dialogs, it is not necessary to create an ODWindow. The Macintosh Toolbox routines Alert() and ModalDialog() take an event filter routine as a parameter, so an ODWindow is not required to get events back to the part. The part should still request the modal focus for one of its frames, and in this case the frame which displayed the dialog is the most approriate choice. Alternatively, the part could create the dialog window, register it with the window state, and request the modal focus for the root frame of the window.  
  16.  
  17. If a dialog event filter is used, it can call the Dispatcher to handle null events, update events and activate events for other windows, but should not call it for other events, because you wouldn't want a part embedded in the window behind the dialog to get mouse clicks, for example.
  18.  
  19. New in DR2: The utility files DlogUtil.h and DlogUtil.cpp contain a fairly robust routine for loading dialogs (ODGetNewDialog) and an accompanying filter proc which supports pseudo-moveable modals.
  20.  
  21. New in DR3: See the recipe Shared Utility Windows
  22.  
  23. As a consequence of OpenDoc's implementation of floating windows, part editors must call ODWindowState::DeactivateFrontWindows() before displaying a modal dialog, and ODWindowState::ActivateFrontWindows() after dismissing it. These methods operate on all floating windows and the frontmost non-floating window.
  24.  
  25. The following method of the Clock part displays the “About Box”.
  26.  
  27. void ClockPart::ShowAboutBox(Environment* ev, ODFrame* frame)
  28. {
  29.     TempODFrame currentOwner = fArbitrator->AcquireFocusOwner(ev, fModalFocus);
  30.     if (fArbitrator->RequestFocus(ev, fModalFocus,frame))
  31.     {
  32.         short itemHit;
  33.         ParamText("\pClock Part…",0,0,0);
  34.         SetCursor(&ODQDGlobals.arrow);
  35.         ModalFilterUPP modalFilter = NewModalFilterProc(AboutDialogFilter);
  36.         
  37.         ODSLong        savedRefNum;
  38.         BeginUsingLibraryResources(savedRefNum);
  39.         DialogPtr aboutDialog = GetNewDialog(kClock_AboutBoxID ,0,(WindowPtr)-1L);
  40.         EndUsingLibraryResources(savedRefNum);
  41.  
  42.         if (aboutDialog)
  43.         {
  44.             fWindowState->DeactivateFrontWindows(ev);    // Can be after GetNewDialog because
  45.                                     // Dialog is create invisible.
  46.             ShowWindow(aboutDialog);
  47.             do
  48.             {
  49.                 ModalDialog(modalFilter, &itemHit);
  50.             } while (itemHit != 1);
  51.             DisposeDialog(aboutDialog);
  52.             fWindowState->ActivateFrontWindows(ev);
  53.         }
  54.         DisposeRoutineDescriptor(modalFilter);
  55.  
  56.         fArbitrator->TransferFocus(ev, fModalFocus,frame, currentOwner);        
  57.     }
  58.     else
  59.         SysBeep(2);
  60. }
  61.  
  62. In this case, the part could relinquish the modal focus instead of transferring it back to its previous owner, but by saving and restoring the owner of the modal focus, this recipe should work for nested modal dialogs, if a dialog was itself built from parts.
  63.  
  64. Note: The Clock Part uses a PICT for its about box. It calls ModalDialog() in a loop, and dismisses the dialog on mouse up so that there isn't a lingering mouse up to erroneously activate a part once the dialog is dismissed.
  65.  
  66. New in DR3:  The dispatcher caches the facet that receives a mouse down, and mouse up events generally go to the same facet, so it’s important not to dismiss dialogs on mouse down because this would invalidate the facet cached by the dispatcher.
  67.  
  68. Dialog Filter
  69.  
  70. The dialog filter can call the Dispatcher to handle null events, update events and activate events for other windows. 
  71.  
  72. pascal Boolean AboutDialogFilter(DialogPtr dialog,
  73.                               EventRecord* event,
  74.                               short* itemHit)
  75. {
  76.     ODBoolean handled = kODFalse;
  77.     
  78.     Environment* ev = somGetGlobalEnvironment();
  79.     
  80.     if (event->what == nullEvent)
  81.     {
  82.         gClockGlobals->GetSession(ev)->GetDispatcher(ev)->Dispatch(ev, (ODEventData*)event);
  83.     }
  84.     else if (event->what == updateEvt &&
  85.                 (WindowPtr) event->message != dialog)        
  86.     {
  87.         gClockGlobals->GetSession(ev)->GetDispatcher(ev)->Dispatch(ev, (ODEventData*)event);
  88.     }
  89.     else if (event->what == activateEvt &&
  90.                 (WindowPtr) event->message != dialog)        
  91.     {
  92.         gClockGlobals->GetSession(ev)->GetDispatcher(ev)->Dispatch(ev, (ODEventData*)event);
  93.     }
  94.     else if (event->what == mouseDown) 
  95.     {
  96.         handled = kODTrue;
  97.         *itemHit = 0;
  98.     }
  99.     else if (event->what == mouseUp) 
  100.     {
  101.         handled = kODTrue;
  102.         *itemHit = 1;
  103.     }
  104.     else if ((event->what == keyDown) || (event->what == autoKey))
  105.     {
  106.         handled = kODTrue;
  107.         *itemHit = 1;
  108.         
  109.     }
  110.  
  111.     return handled;
  112. }
  113.  
  114. The part editor will need access to the session. Using a global is fine, but the the refCon of the window could be used instead.
  115.  
  116. Relinquishing the Modal Focus
  117.  
  118. Parts will generally be unwilling to relinquish the modal focus. In Part::BeginRelinquishFocus:
  119.  
  120.     if (focus == fModalFocus))
  121.         return kODFalse;
  122.  
  123. Movable Modal Dialogs
  124.  
  125. A movable modal dialog which strictly follows the Macintosh Human Interface guidelines allows the user to switch out to other processes. To implement such a dialog, ModalDialog() can not be used, and an ODWindow must be creates as shown below.
  126.  
  127. New in DR2:  The filter proc in DlogUtil.cpp can be used with ModalDialog() and  modal dialogs which can move but do not allow switching out to other processes. Many reasonable people consider this quite adequate, and certainly preferable to a modal dialog which can’t move at all.
  128.  
  129. Displaying the Dialog
  130.  
  131. The following method of the Clock part displays the Synchronize… dialog, which is a true movable modal.
  132.  
  133. void ClockPart::ShowSynchDialog(Environment* ev)
  134. {
  135.     ODSLong savedRefNum;
  136.     BeginUsingLibraryResources(savedRefNum);
  137.     
  138.     DialogPtr synchDialog = GetNewDialog(kClock_SynchronizeDialogID ,
  139.                     (Ptr)ODNewPtr(sizeof(DialogRecord)), 
  140.                     (WindowPtr)-1L);
  141.         EndUsingLibraryResources(savedRefNum);
  142.     ODWindow* synchWindow = fWindowState->
  143.                                     RegisterWindow(ev, synchDialog,
  144.                                         kODNonPersistentFrameObject,
  145.                                          kODFalse,    // Keeps draft open
  146.                                          kODFalse,    // Is resizable
  147.                                          kODFalse,    // Is floating
  148.                                          kODFalse, // don't save
  149.                                          kODFalse,    // should dispose
  150.                                          fPartWrapper, 
  151.                                          fFrameView, 
  152.                                          fSynchronizePresentation, 
  153.                                          kODNULL);
  154.     
  155.     if (synchDialog 
  156.         && (fArbitrator->RequestFocus(ev, fModalFocus,
  157.                     synchWindow->GetRootFrame(ev))))
  158.  
  159.     {
  160.         fMenuBar->DisableAll(ev);
  161.         fMenuBar->EnableCommand(ev,  kODCommandEditMenu, kODTrue ) ;
  162.         InvalMenuBar();
  163.         
  164.         SetCursor(&ODQDGlobals.arrow);
  165.         synchWindow->Open(ev);
  166.         synchWindow->Show(ev);
  167.         synchWindow->Select(ev);
  168.         ODReleaseObject(ev, synchWindow);
  169.         
  170.     }
  171. }
  172.  
  173. Dismissing the Dialog
  174.  
  175. When a mouse-down is received:
  176.  
  177. ODBoolean ClockSynchroDialogFrame::HandleMouseDown(Environment* ev, ODFacet* facet, ODEventData* event)
  178. {
  179.     ODBoolean wasHandled = kODFalse;
  180.  
  181.     if (IsDialogEvent((const EventRecord *)event))
  182.     {
  183.         short itemHit;
  184.         DialogPtr dialog;
  185.         wasHandled = DialogSelect((const EventRecord *)event, &dialog, &itemHit);
  186.         ODWindow* window = fWindowState->AcquireODWindow(ev, dialog);         
  187.         if (itemHit == cancel)
  188.         {
  189.             ClockPart* clockPart = fClockPart; // "this" is about to go away
  190.             fArbitrator->RelinquishFocus(ev, fModalFocus, fFrame);
  191.             window->CloseAndRemove(ev);
  192.             // ODReleaseObject(ev, window); // released by CloseAndRemove
  193.             
  194.             clockPart->GetMenuBar()->EnableAll(ev);
  195.  
  196.             InvalMenuBar();
  197.         }
  198.         else
  199.             ODReleaseObject(ev, window);
  200.     }
  201.  
  202.     return wasHandled;
  203. }
  204.  
  205. Note: The dialog is only dismissed on mouse up, because of the call to DialogSelect().
  206.  
  207. Modeless Dialogs
  208.  
  209. Showing the dialog
  210.  
  211. Suppose the user chooses a menu item to show a modeless dialog. The part editor should do something like the following method of the Clock part, which shows the Alarm Settings dialog:
  212.  
  213. void ClockGlobals::OpenAlarmSettingsDialog(Environment* ev, ODFrame* sourceFrame)
  214. {
  215.     ODPart* sourcePart = sourceFrame->AcquirePart(ev);
  216.     ODWindow* window = fSession->GetWindowState(ev)->AcquireWindow(ev, fAlarmSettingsWindow);
  217.     if (window)
  218.     {
  219.         window->Show(ev);
  220.         window->Select(ev);
  221.         ODReleaseObject(ev, window);
  222.     }
  223.     else
  224.     {
  225.         this->CreateAlarmSettingsDialog(ev, sourceFrame);
  226.         this->OpenAlarmSettingsDialog(ev, sourceFrame); // Ooh. Sneaky
  227.     }    
  228.     ODReleaseObject(ev, sourcePart);
  229.  
  230.  
  231. }
  232.  
  233. void ClockGlobals::CreateAlarmSettingsDialog(Environment* ev, ODFrame* sourceFrame)
  234. {
  235.     ODSLong savedRefNum;
  236.     ODWindow* settingsWindow = kODNULL;
  237.     ODPart* sourcePart = sourceFrame->AcquirePart(ev);
  238.     
  239.     BeginUsingLibraryResources(savedRefNum);
  240.     fAlarmSettingsDialog = GetNewDialog(kClock_AlarmSettingsDialogID ,                                                 (Ptr)ODNewPtr(sizeof(DialogRecord)),
  241.                         (WindowPtr)-1L);
  242.     EndUsingLibraryResources(savedRefNum);
  243.     
  244.     settingsWindow = fSession->GetWindowState(ev)->
  245.                         RegisterWindow(ev, fAlarmSettingsDialog, 
  246.                             kODNonPersistentFrameObject,
  247.                              kODFalse,    // Keeps draft open
  248.                              kODFalse,    // Is resizable
  249.                              kODFalse,    // Is floating
  250.                              kODFalse,    // don't save
  251.                              kODFalse,    // should dispose
  252.                              sourcePart, 
  253.                              fFrameView, // View Type
  254.                              fAlarmSettingsPresentation, // Presentation
  255.                              sourceFrame);
  256.  
  257.     
  258.     if (fAlarmSettingsDialog && settingsWindow)
  259.     {
  260.         settingsWindow->Open(ev);
  261.         fAlarmSettingsWindow = settingsWindow->GetID(ev);
  262.         ODReleaseObject(ev, settingsWindow);
  263.     }
  264.     ODReleaseObject(ev, sourcePart);
  265. }
  266.  
  267. Click in Close Box
  268.  
  269. When the user clicks in the close box of the modeless dialog, the part may wish to hide the window rather than close it, so that it is not destroyed and can be redisplayed rapidly.
  270.  
  271. In Part::HandleEvent:
  272.  
  273.         case kODEvtWindow: 
  274.             {
  275.                 switch (event->message)
  276.                 {
  277.                     case inGoAway:
  278.                         wasHandled = _fClockPart->CloseWindow(ev, frame);
  279.                         break;
  280.  
  281. In menu-handling code:
  282.  
  283.         case kODCommandClose:
  284.             wasHandled = fClockPart->CloseWindow(ev, fFrame);
  285.             break;
  286.  
  287. ODBoolean ClockPart::CloseWindow(Environment* ev, ODFrame* frame)
  288. {
  289.     ODBoolean         handled            = kODFalse;
  290.     ODTypeToken presentation = frame->GetPresentation(ev);
  291.     
  292.     if (presentation == fDisplaySettingsPresentation)
  293.     {
  294.         gClockGlobals->CloseDisplaySettingsWindow(ev);
  295.         handled = kODTrue;
  296.     }
  297.     else if (presentation == fAlarmSettingsPresentation)
  298.     {
  299.         gClockGlobals->CloseAlarmSettingsWindow(ev);
  300.         handled = kODTrue;
  301.     }
  302.         
  303.     return handled;
  304. }
  305.  
  306.  
  307. void ClockGlobals::CloseAlarmSettingsWindow(Environment* ev)
  308. {
  309.     ODWindow* window = fSession->GetWindowState(ev)->AcquireWindow(ev, fAlarmSettingsWindow);
  310.     if (window)
  311.     {
  312.         window->Hide(ev);
  313.     }
  314.     ODReleaseObject(ev, window);
  315. }
  316.  
  317.  
  318. When activating  or deactivating a frame
  319.  
  320. Parts should hide their palettes and dialogs when deactivated, and also when the process is deactivated . In the Test Clock example, the dialogs are shared between part instances. See also Shared Windows recipe.
  321.  
  322. When the selection focus is lost or gained, the global object managing the dialogs is notified:
  323.  
  324. void ClockFrame::LostSelectionFocus(Environment* ev, ODFrame* proposedFrame)
  325. {
  326.     ODBoolean samePart = 
  327.         (proposedFrame && (proposedFrame->GetPresentation(ev) == fClockPart->fTimePresentation));
  328.     if (!samePart)
  329.         gClockGlobals->SuspendWindows(ev, kODFalse);
  330. }
  331.  
  332. void ClockFrame::AcquiredSelectionFocus(Environment* ev)
  333. {
  334.     gClockGlobals->AcquiringFocus(ev, fFrame);
  335.     gClockGlobals->ResumeWindows(ev, kODFalse);
  336. }
  337.  
  338. The global object delegates to methods of the ClockFrame class stored in the part info:
  339.  
  340. void ClockGlobals::SuspendWindows(Environment* ev, ODBoolean processChange)
  341. {
  342.     OrderedCollectionIterator iter(fDialogFrames);
  343.     
  344.     for (ODFrame* aFrame = (ODFrame*)iter.First(); 
  345.                             iter.IsNotComplete();
  346.                             aFrame = (ODFrame*)iter.Next())
  347.     {
  348.         ClockFrame* clockFrame = (ClockFrame*) aFrame->GetPartInfo(ev);
  349.         if (processChange)
  350.             clockFrame->SuspendProcess(ev);
  351.         else
  352.             clockFrame->SuspendFocus(ev);
  353.     }
  354.  
  355. }
  356.  
  357. void ClockGlobals::ResumeWindows(Environment* ev, ODBoolean processChange)
  358. {
  359.     OrderedCollectionIterator iter(fDialogFrames);
  360.     
  361.     for (ODFrame* aFrame = (ODFrame*)iter.First(); 
  362.                             iter.IsNotComplete();
  363.                             aFrame = (ODFrame*)iter.Next())
  364.     {
  365.         ClockFrame* clockFrame = (ClockFrame*) aFrame->GetPartInfo(ev);
  366.         if (processChange)
  367.             clockFrame->ResumeProcess(ev);
  368.         else
  369.             clockFrame->ResumeFocus(ev);
  370.     }
  371. }
  372.  
  373. When a suspend or resume event is received, the frame objects are notified directly:
  374.  
  375.         case kSuspendResumeMessage:
  376.             {
  377.                 ClockFrame* clockFrame = (ClockFrame*) frame->GetPartInfo(ev);
  378.                 const short kResumeMask = 0x01;    // High byte suspend/resume event 
  379.                 ODBoolean    goingToBackground = (event->message & kResumeMask) == 0;
  380.                 
  381.                 if (goingToBackground)
  382.                 {
  383.                     clockFrame->SuspendProcess(ev);    
  384.                 }
  385.                 else
  386.                 {
  387.                     clockFrame->ResumeProcess(ev);    
  388.                 }
  389.             }
  390.  
  391. The frame objects hide and show their windows as needed:
  392.  
  393. void ClockFrame::SuspendFocus(Environment* ev)
  394. {
  395.     if (fFrame->IsRoot(ev) && fShouldHideOnSuspend)
  396.     {    
  397.         TempODWindow window = fFrame->AcquireWindow(ev);
  398.         if (window->IsShown(ev))
  399.         {
  400.             window->Hide(ev);
  401.             fShowWindowOnFocus = kODTrue;
  402.         }
  403.         else
  404.         {
  405.             fShowWindowOnFocus = kODFalse;
  406.         }
  407.     }
  408. }
  409.  
  410.  
  411. void ClockFrame::SuspendProcess(Environment* ev)
  412. {
  413.     fInBackground = kODTrue;
  414.     if (fFrame->IsRoot(ev) && fShouldHideOnSuspend)
  415.     {    
  416.         TempODWindow window = fFrame->AcquireWindow(ev);
  417.         if (window->IsShown(ev))
  418.         {
  419.             fShowWindowOnResume = kODTrue;
  420.             window->Hide(ev);
  421.         }
  422.         else
  423.         {
  424.             fShowWindowOnResume = kODFalse; 
  425.         }
  426.     }
  427. }
  428.  
  429. void ClockFrame::ResumeFocus(Environment* ev)
  430. {
  431.     if (fFrame->IsRoot(ev) && fShouldHideOnSuspend)
  432.     {    
  433.         TempODWindow window = fFrame->AcquireWindow(ev);
  434.         if (fShowWindowOnFocus && !fInBackground)
  435.         {
  436.             fShowWindowOnFocus = kODFalse; 
  437.             window->Show(ev);
  438.         }
  439.     }
  440. }
  441.  
  442. void ClockFrame::ResumeProcess(Environment* ev)
  443. {
  444.     fInBackground = kODFalse;
  445.     if (fFrame->IsRoot(ev) && fShouldHideOnSuspend)
  446.     {    
  447.         TempODWindow window = fFrame->AcquireWindow(ev);
  448.         if (fShowWindowOnResume)
  449.         {
  450.             fShowWindowOnResume = kODFalse; // It may be hidden by user before next Suspend
  451.             fShowWindowOnFocus = kODFalse;
  452.             window->Show(ev);
  453.         }
  454.     }
  455. }
  456.